#pragma once
#include <sstream>

#include "basic.hpp"

namespace rvfft {

class OpcodeBase
{
public:
RET_OPCODE concat_opcode(
  Opcode<16> imm_16,
  Opcode<4>  funct_4,
  Opcode<5>  rd_5,
  Opcode<7>  opcode_7
)
{
  std::stringstream tmp;
  tmp << imm_16 << funct_4 << rd_5 << opcode_7;
  return tmp.str();
}

RET_OPCODE concat_opcode(
  Opcode<12> imm_12,
  Opcode<5>  rs1_5,
  Opcode<3>  funct3_3,
  Opcode<5>  rd_5,
  Opcode<7>  opcode_7
)
{
  std::stringstream tmp;
  tmp << imm_12 << rs1_5 << funct3_3 << rd_5 << opcode_7;
  return tmp.str();
}

RET_OPCODE concat_opcode(
  Opcode<7> imm_7,
  Opcode<5> rs2_5,
  Opcode<5> rs1_5,
  Opcode<3> funct3_3,
  Opcode<5> rd_5,
  Opcode<7> opcode_7
)
{
  std::stringstream tmp;
  tmp << imm_7 << rs2_5 << rs1_5 << funct3_3 << rd_5 << opcode_7;
  return tmp.str();
}

RET_OPCODE concat_opcode(
  Opcode<4> imm_4,
  Opcode<3> imm_3,
  Opcode<5> rs2_5,
  Opcode<5> rs1_5,
  Opcode<3> funct3_3,
  Opcode<5> rd_5,
  Opcode<7> opcode_7
)
{
  std::stringstream tmp;
  tmp << imm_4 << imm_3 << rs2_5 << rs1_5 << funct3_3 << rd_5 << opcode_7;
  return tmp.str();
}

};

class InstrBase : public OpcodeBase
{
public:
RET_OPCODE shuffle(auto imm_4, auto imm_3, auto rs2_5, auto rs1_5, auto rd_5)
{
  return concat_opcode(imm_4, imm_3, rs2_5, rs1_5, 0b110, rd_5, OPCODE_shuf);
}

RET_OPCODE butterfly(auto imm_4, auto imm_3, auto rs2_5, auto rs1_5, auto rd_5)
{
  return concat_opcode(imm_4, imm_3, rs2_5, rs1_5, 0b101, rd_5, OPCODE_butt);
}

RET_OPCODE complex(auto imm_7, auto rs2_5, auto rs1_5, auto funct3_3, auto rd_5)
{
  return concat_opcode(imm_7, rs2_5, rs1_5, funct3_3, rd_5, OPCODE_comp);
}

RET_OPCODE ld_vec(auto imm_12, auto rs1_5, auto funct3_3, auto rd_5)
{
  return concat_opcode(imm_12, rs1_5, funct3_3, rd_5, OPCODE_ldst);
}

RET_OPCODE st_vec(auto imm_7, auto rs2_5, auto rs1_5, auto funct3_3, auto rd_5)
{
  return concat_opcode(imm_7, rs2_5, rs1_5, funct3_3, rd_5, OPCODE_ldst);
}

RET_OPCODE test_w(auto imm_16, auto funct_4, auto rd_5)
{
  return concat_opcode(imm_16, funct_4, rd_5, OPCODE_test);
}

};

}